/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.server.router.proxy.aggragate; import static org.kie.server.router.utils.Helper.read; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.StringReader; import java.util.List; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.jboss.logging.Logger; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public abstract class XMLResponseAggregator implements ResponseAggregator { private static final Logger log = Logger.getLogger(XMLResponseAggregator.class); private static final String SORT_XSLT = read(XMLResponseAggregator.class.getResourceAsStream("/sort.xsl")); private static final String PAGE_XSLT = read(XMLResponseAggregator.class.getResourceAsStream("/page.xsl")); public XMLResponseAggregator() { } protected boolean supports(String expectedType, Object... acceptTypes) { for (Object acceptType : acceptTypes ) { if (acceptType == null) { continue; } boolean found = acceptType.toString().toLowerCase().contains(expectedType); if (found) { return true; } } return false; } public String aggregate(List<String> data) { try { if (data == null || data.isEmpty()) { return null; } List<String> nodes = knownNames(); Document document = data.stream().map(xml -> { return newDoc(xml); }) .filter(d -> d != null) .reduce((source, target) -> { deepMerge(source, target, nodes, target); return target; }).get(); ByteArrayOutputStream out = new ByteArrayOutputStream(); Transformer transformer = TransformerFactory.newInstance().newTransformer(); Result output = new StreamResult(out); Source input = new DOMSource(document); transformer.transform(input, output); return new String(out.toByteArray()); } catch (Exception e) { log.errorf("Failed to aggregate xml responses of %s", data, e); throw new RuntimeException(e); } } @Override public String aggregate(List<String> data, String sortBy, boolean ascending, Integer page, Integer pageSize) { try { if (data == null || data.isEmpty()) { return null; } List<String> nodes = knownNames(); Document document = data.stream().map(xml -> { return newDoc(xml); }) .filter(d -> d != null) .reduce((source, target) -> { deepMerge(source, target, nodes, target); return target; }).get(); ByteArrayOutputStream out = new ByteArrayOutputStream(); Transformer transformer = null; String root = document.getDocumentElement().getNodeName(); String sortNode = getElementLevel(root); if (sortBy != null && !sortBy.trim().isEmpty()) { transformer = sort(getRootNode(root), sortNode, sortBy, ascending, document); DOMResult toutput = new DOMResult(); Source input = new DOMSource(document); transformer.transform(input, toutput); document = (Document) toutput.getNode(); } transformer = page(getRootNode(root), sortNode, page, pageSize, document); Result output = new StreamResult(out); transformer.transform(new DOMSource(document), output); return new String(out.toByteArray()); } catch (Exception e) { log.errorf("Failed to aggregate xml responses of %s", data, e); throw new RuntimeException(e); } } protected Transformer sort(String root, String level, String fieldName, boolean ascending, Document source) throws Exception { String order = "ascending"; if (!ascending) { order = "descending"; } StreamSource xsltSort = new StreamSource(new StringReader(SORT_XSLT .replaceAll("KIE_ROOT", root) .replaceAll("LEVEL", level) .replaceAll("KIE_SORT_BY", sortBy(fieldName)) .replaceAll("KIE_ORDER", order))); return TransformerFactory.newInstance().newTransformer(xsltSort); } protected Transformer page(String root, String node, Integer start, Integer size, Document source) throws Exception { Integer actualStart = start; Integer actualSize = size; if (actualStart > 0) { actualStart = actualStart * size; actualSize = actualStart + size; } log.debug("Start " + actualStart + " size " + actualSize); StreamSource xsltSort = new StreamSource(new StringReader(PAGE_XSLT .replaceAll("LEVEL", node) .replaceAll("START", actualStart.toString()) .replaceAll("SIZE", actualSize.toString()))); return TransformerFactory.newInstance().newTransformer(xsltSort); } public Document newDoc(String xml) { try (ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes("UTF-8"))) { Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stream); return doc; } catch (Exception e) { log.errorf("Failed to create xml document of %s", xml, e); return null; } } protected void deepMerge(Node sourceNode, Node targetNode, List<String> nodes, Document target) { if (targetNode == null) { return; } NodeList sourceNodeList = sourceNode.getChildNodes(); NodeList targetNodeList = targetNode.getChildNodes(); for (int i = 0; i < sourceNodeList.getLength(); i++) { sourceNode = sourceNodeList.item(i); targetNode = targetNodeList.item(i); if (!nodes.contains(sourceNode.getNodeName())) { deepMerge(sourceNode, targetNode, nodes, target); } if (targetNode == null) { return; } // found correct node to copy it's content NodeList children = sourceNode.getChildNodes(); copyNodes(children, target, targetNode); } } protected abstract void copyNodes(NodeList children, Document target, Node targetNode); protected abstract List<String> knownNames(); protected abstract String getElementLevel(String rootNode); protected abstract String getRootNode(String rootNode); protected abstract String sortBy(String fieldName); }